package search;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import act.End;
import act.IActor;
import act.IReceiver;

public class Grep implements IReceiver {

	// Charset and decoder for ISO-8859-15
	private final static Charset charset = Charset.forName("ISO-8859-15");

	private final static CharsetDecoder decoder = charset.newDecoder();

	// Pattern used to parse lines
	private final static Pattern linePattern = Pattern.compile(".*\r?\n");

	private final Pattern pattern;

	private final IActor target;

	private IActor actor;

	public Grep(Pattern pattern, IActor target) {
		this.pattern = pattern;
		this.target = target;
	}

	@Override
	public void receive(Object msg) {

		if (msg instanceof String) {
			grep((String) msg);// grep the file whose name is msg
		} else if (msg instanceof End)// the source stream is finished so stop the message pump.
		{			
			this.actor.stop();
			target.send(msg);//tell the target we are finished sending messages
		}
	}

	private void grep(String filePath) {
		
		//This grep code was taken from a sample on the sun/oracle site
		
		File f = new File(filePath);

		// Open the file and then get a channel from the stream

		FileChannel fc = null;
		try {
			FileInputStream fis = new FileInputStream(f);
			fc = fis.getChannel();

			// Get the file's size and then map it into memory
			int sz = (int) fc.size();
			MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);

			// Decode the file into a char buffer
			CharBuffer cb = decoder.decode(bb);

			// Perform the search
			grep(f, cb, pattern);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// Close the channel and the stream
			if (fc != null) {
				try {
					fc.close();
				} catch (IOException ignore) {
					ignore.printStackTrace();
				}
			}
		}

	}

	// Use the linePattern to break the given CharBuffer into lines, applying
	// the input pattern to each line to see if we have a match
	//
	private void grep(File f, CharBuffer cb, Pattern pattern) {
		Matcher lm = linePattern.matcher(cb); // Line matcher
		Matcher pm = null; // Pattern matcher
		int lines = 0;
		while (lm.find()) {
			lines++;
			CharSequence cs = lm.group(); // The current line
			if (pm == null)
				pm = pattern.matcher(cs);
			else
				pm.reset(cs);
			if (pm.find()) {
				if (target != null)
					target.send(f + ":" + lines + ":" + cs);
			}
			if (lm.end() == cb.limit())
				break;
		}
	}

	@Override
	public void setActor(IActor actor) {
		this.actor = actor;
	}

}